package com.reactim.message.security;

import com.reactim.message.connection.ConnectionInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.Instant;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * 连接验证组件
 * 提供连接请求的验证和安全检查功能
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class ConnectionValidator {
    
    // 用户连接频率限制缓存 (userId -> 最后连接时间)
    private final ConcurrentHashMap<Long, Instant> userConnectionAttempts = new ConcurrentHashMap<>();
    
    // IP连接频率限制缓存 (IP -> 连接次数和时间窗口)
    private final ConcurrentHashMap<String, ConnectionAttempt> ipConnectionAttempts = new ConcurrentHashMap<>();
    
    // 黑名单IP集合
    private final Set<String> blacklistedIps = ConcurrentHashMap.newKeySet();
    
    @Value("${connection.security.validation.max-connections-per-user:5}")
    private int maxConnectionsPerUser;
    
    @Value("${connection.security.validation.connection-interval-seconds:5}")
    private int connectionIntervalSeconds;
    
    @Value("${connection.security.validation.max-connections-per-ip:20}")
    private int maxConnectionsPerIp;
    
    @Value("${connection.security.validation.ip-rate-limit-window-minutes:10}")
    private int ipRateLimitWindowMinutes;
    
    @Value("${connection.security.validation.enable-device-validation:true}")
    private boolean enableDeviceValidation;
    
    @Value("${connection.security.validation.enable-version-validation:true}")
    private boolean enableVersionValidation;
    
    @Value("${connection.security.validation.min-client-version:1.0.0}")
    private String minClientVersion;
    
    // 设备信息验证正则表达式
    private static final Pattern DEVICE_INFO_PATTERN = Pattern.compile("^[a-zA-Z0-9\\s\\-_.,()]+$");
    
    // 版本号验证正则表达式
    private static final Pattern VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$");
    
    /**
     * 验证连接请求
     * 
     * @param userId 用户ID
     * @param connectionInfo 连接信息
     * @param clientIp 客户端IP
     * @return 验证结果
     */
    public Mono<ValidationResult> validateConnection(Long userId, ConnectionInfo connectionInfo, String clientIp) {
        return Mono.fromCallable(() -> {
            // 1. 检查用户ID有效性
            if (userId == null || userId <= 0) {
                return ValidationResult.failure("无效的用户ID");
            }
            
            // 2. 检查IP黑名单
            if (isIpBlacklisted(clientIp)) {
                log.warn("黑名单IP尝试连接: ip={}, userId={}", clientIp, userId);
                return ValidationResult.failure("IP已被禁止访问");
            }
            
            // 3. 检查用户连接频率限制
            ValidationResult userRateResult = validateUserConnectionRate(userId);
            if (!userRateResult.isValid()) {
                return userRateResult;
            }
            
            // 4. 检查IP连接频率限制
            ValidationResult ipRateResult = validateIpConnectionRate(clientIp);
            if (!ipRateResult.isValid()) {
                return ipRateResult;
            }
            
            // 5. 验证连接信息
            ValidationResult connectionInfoResult = validateConnectionInfo(connectionInfo);
            if (!connectionInfoResult.isValid()) {
                return connectionInfoResult;
            }
            
            // 6. 记录连接尝试
            recordConnectionAttempt(userId, clientIp);
            
            log.debug("连接验证通过: userId={}, ip={}", userId, clientIp);
            return ValidationResult.success();
            
        }).onErrorReturn(ValidationResult.failure("连接验证过程中发生错误"));
    }
    
    /**
     * 验证用户连接频率
     */
    private ValidationResult validateUserConnectionRate(Long userId) {
        Instant now = Instant.now();
        Instant lastAttempt = userConnectionAttempts.get(userId);
        
        if (lastAttempt != null) {
            Duration timeSinceLastAttempt = Duration.between(lastAttempt, now);
            if (timeSinceLastAttempt.getSeconds() < connectionIntervalSeconds) {
                log.warn("用户连接过于频繁: userId={}, timeSinceLastAttempt={}s", 
                        userId, timeSinceLastAttempt.getSeconds());
                return ValidationResult.failure("连接请求过于频繁，请稍后再试");
            }
        }
        
        return ValidationResult.success();
    }
    
    /**
     * 验证IP连接频率
     */
    private ValidationResult validateIpConnectionRate(String clientIp) {
        Instant now = Instant.now();
        ConnectionAttempt attempt = ipConnectionAttempts.computeIfAbsent(clientIp, 
                k -> new ConnectionAttempt());
        
        // 清理过期的连接记录
        if (Duration.between(attempt.windowStart, now).toMinutes() >= ipRateLimitWindowMinutes) {
            attempt.reset(now);
        }
        
        // 检查连接次数限制
        if (attempt.count >= maxConnectionsPerIp) {
            log.warn("IP连接次数超限: ip={}, count={}, limit={}", 
                    clientIp, attempt.count, maxConnectionsPerIp);
            return ValidationResult.failure("IP连接次数超限，请稍后再试");
        }
        
        return ValidationResult.success();
    }
    
    /**
     * 验证连接信息
     */
    private ValidationResult validateConnectionInfo(ConnectionInfo connectionInfo) {
        if (connectionInfo == null) {
            return ValidationResult.failure("连接信息不能为空");
        }
        
        // 验证设备信息
        if (enableDeviceValidation) {
            ValidationResult deviceResult = validateDeviceInfo(connectionInfo.getDeviceInfo());
            if (!deviceResult.isValid()) {
                return deviceResult;
            }
        }
        
        // 验证客户端版本
        if (enableVersionValidation) {
            ValidationResult versionResult = validateClientVersion(connectionInfo.getClientVersion());
            if (!versionResult.isValid()) {
                return versionResult;
            }
        }
        
        // 验证时间戳
        if (connectionInfo.getTimestamp() == null) {
            return ValidationResult.failure("连接时间戳不能为空");
        }
        
        // 检查时间戳是否合理（不能太久之前或未来）
        long now = System.currentTimeMillis();
        long timeDiff = Math.abs(now - connectionInfo.getTimestamp());
        if (timeDiff > Duration.ofMinutes(5).toMillis()) {
            return ValidationResult.failure("连接时间戳异常");
        }
        
        return ValidationResult.success();
    }
    
    /**
     * 验证设备信息
     */
    private ValidationResult validateDeviceInfo(String deviceInfo) {
        if (deviceInfo == null || deviceInfo.trim().isEmpty()) {
            return ValidationResult.failure("设备信息不能为空");
        }
        
        if (deviceInfo.length() > 200) {
            return ValidationResult.failure("设备信息长度超限");
        }
        
        if (!DEVICE_INFO_PATTERN.matcher(deviceInfo).matches()) {
            return ValidationResult.failure("设备信息格式无效");
        }
        
        return ValidationResult.success();
    }
    
    /**
     * 验证客户端版本
     */
    private ValidationResult validateClientVersion(String clientVersion) {
        if (clientVersion == null || clientVersion.trim().isEmpty()) {
            return ValidationResult.failure("客户端版本不能为空");
        }
        
        if (!VERSION_PATTERN.matcher(clientVersion).matches()) {
            return ValidationResult.failure("客户端版本格式无效");
        }
        
        // 检查最低版本要求
        if (isVersionLowerThan(clientVersion, minClientVersion)) {
            return ValidationResult.failure("客户端版本过低，请升级到 " + minClientVersion + " 或更高版本");
        }
        
        return ValidationResult.success();
    }
    
    /**
     * 比较版本号
     */
    private boolean isVersionLowerThan(String version1, String version2) {
        String[] v1Parts = version1.split("\\.");
        String[] v2Parts = version2.split("\\.");
        
        for (int i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
            int v1Part = i < v1Parts.length ? Integer.parseInt(v1Parts[i]) : 0;
            int v2Part = i < v2Parts.length ? Integer.parseInt(v2Parts[i]) : 0;
            
            if (v1Part < v2Part) {
                return true;
            } else if (v1Part > v2Part) {
                return false;
            }
        }
        
        return false; // 版本相等
    }
    
    /**
     * 记录连接尝试
     */
    private void recordConnectionAttempt(Long userId, String clientIp) {
        Instant now = Instant.now();
        
        // 记录用户连接时间
        userConnectionAttempts.put(userId, now);
        
        // 记录IP连接次数
        ConnectionAttempt attempt = ipConnectionAttempts.get(clientIp);
        if (attempt != null) {
            attempt.count++;
        }
    }
    
    /**
     * 检查IP是否在黑名单中
     */
    private boolean isIpBlacklisted(String ip) {
        return blacklistedIps.contains(ip);
    }
    
    /**
     * 添加IP到黑名单
     */
    public void addToBlacklist(String ip) {
        blacklistedIps.add(ip);
        log.info("IP已添加到黑名单: {}", ip);
    }
    
    /**
     * 从黑名单移除IP
     */
    public void removeFromBlacklist(String ip) {
        blacklistedIps.remove(ip);
        log.info("IP已从黑名单移除: {}", ip);
    }
    
    /**
     * 清理过期的连接记录
     */
    public void cleanupExpiredRecords() {
        Instant now = Instant.now();
        
        // 清理用户连接记录
        userConnectionAttempts.entrySet().removeIf(entry -> 
                Duration.between(entry.getValue(), now).toMinutes() > 60);
        
        // 清理IP连接记录
        ipConnectionAttempts.entrySet().removeIf(entry -> 
                Duration.between(entry.getValue().windowStart, now).toMinutes() >= ipRateLimitWindowMinutes);
        
        log.debug("清理过期连接记录完成");
    }
    
    /**
     * 连接尝试记录
     */
    private static class ConnectionAttempt {
        private int count = 0;
        private Instant windowStart = Instant.now();
        
        public void reset(Instant now) {
            this.count = 0;
            this.windowStart = now;
        }
    }
    
    /**
     * 验证结果
     */
    public static class ValidationResult {
        public static final ValidationResult VALID = new ValidationResult(true, null);
        
        private final boolean valid;
        private final String message;
        
        private ValidationResult(boolean valid, String message) {
            this.valid = valid;
            this.message = message;
        }
        
        public static ValidationResult success() {
            return new ValidationResult(true, null);
        }
        
        public static ValidationResult failure(String message) {
            return new ValidationResult(false, message);
        }
        
        public boolean isValid() {
            return valid;
        }
        
        public String getMessage() {
            return message;
        }
    }
}